from flask import Blueprint, request, jsonify
import pandas as pd
import numpy as np
import random
import time
from concurrent.futures import ProcessPoolExecutor, as_completed
from .back_trade import TradingStrategy, calculate_drawdowns, calculate_monthly_returns, calculate_yearly_returns, get_hs300_data

# 创建蓝图对象
optimization = Blueprint('optimization', __name__)

@optimization.route('/optimization/run', methods=['POST'])
def run_optimization():
    """
    执行参数优化，尝试不同的参数组合来找到最优策略
    """
    try:
        # 从请求中获取参数
        req = request.get_json()
        
        # 验证输入参数
        if not req.get('codes') or len(req.get('codes')) == 0:
            return jsonify({"error": "至少需要选择一个股票代码"}), 400
        
        if not req.get('paramRanges'):
            return jsonify({"error": "需要提供参数范围"}), 400
        
        # 获取周期参数
        cycle = req.get('cycle', 'day')
        
        # 生成参数组合
        parameter_sets = generate_parameter_combinations(
            req.get('paramRanges'), 
            req.get('iterationCount', 50)
        )
        
        # 执行并行回测
        results = []
        
        # 使用进程池进行并行计算
        with ProcessPoolExecutor(max_workers=4) as executor:
            futures = []
            
            for params in parameter_sets:
                # 准备回测参数
                backtest_params = {
                    "codes": req.get('codes'),
                    "cycle": cycle,  # 添加周期参数
                }
                
                # 如果有日期范围，添加到参数中
                date_range = req.get('dateRange')
                if date_range and date_range.get('startDate'):
                    backtest_params["startDate"] = date_range.get('startDate')
                
                # 提交任务到进程池
                futures.append(executor.submit(execute_backtest, backtest_params, params))
            
            # 收集结果
            for i, future in enumerate(as_completed(futures)):
                try:
                    result = future.result()
                    results.append(result)
                except Exception as e:
                    print(f"回测执行错误: {str(e)}")
        
        # 根据优化目标排序结果
        sorted_results = sort_results(results, req.get('optimizationTarget', 'sharpeRatio'))
        
        # 构建响应
        response = {
            "status": "completed",
            "iterations": len(parameter_sets),
            "results": sorted_results,
            "cycle": cycle  # 在响应中包含周期信息
        }
        
        # 如果有日期范围，也添加到响应中
        if date_range:
            response["dateRange"] = date_range
        
        return jsonify(response)
    
    except Exception as e:
        return jsonify({"error": f"参数优化失败: {str(e)}"}), 500

@optimization.route('/optimization/save', methods=['POST'])
def save_optimization_result():
    """
    保存优化结果作为可复用的策略参数
    """
    try:
        # 从请求中获取参数
        data = request.get_json()
        
        # 简单验证
        if "name" not in data or "parameters" not in data:
            return jsonify({"error": "需要提供名称和参数"}), 400
        
        # 保存参数 (实际实现中可以存储到数据库)
        # 这里只是简单返回成功
        
        return jsonify({
            "success": True,
            "message": "参数已成功保存"
        })
        
    except Exception as e:
        return jsonify({"error": f"保存参数失败: {str(e)}"}), 500

def generate_parameter_combinations(param_ranges, iteration_count):
    """
    根据参数范围生成随机参数组合
    """
    combinations = []
    
    for _ in range(iteration_count):
        params = {}
        
        # 对每个参数，从其范围内随机选择一个值
        for param_name, param_range in param_ranges.items():
            if param_name == "ischeckhs300":
                # 布尔值参数
                params[param_name] = random.choice(param_range)
            elif param_name in ["sortType", "tradeType"]:
                # 离散值选择
                params[param_name] = random.choice(param_range)
            else:
                # 数值范围参数
                if len(param_range) == 2 and isinstance(param_range[0], (int, float)):
                    min_val, max_val = param_range
                    if isinstance(min_val, int) and isinstance(max_val, int):
                        params[param_name] = random.randint(min_val, max_val)
                    else:
                        params[param_name] = random.uniform(min_val, max_val)
                else:
                    # 如果是列表选项，随机选择一个
                    params[param_name] = random.choice(param_range)
        
        combinations.append(params)
    
    return combinations

def execute_backtest(backtest_params, original_params):
    """
    执行单次回测并计算评估指标
    """
    try:
        # 获取周期参数
        cycle = backtest_params.get("cycle", "day")
        
        # 准备参数
        params = {
            "ischeckhs300": original_params.get("ischeckhs300", False),
            "hs300_limit": original_params.get("hs300_limit", 30),
            "backCycle": cycle,  # 使用传入的周期参数
            "codes": backtest_params.get("codes", []),
            "increaseday_range": original_params.get("increaseday_range", 21),
            "highday_range": original_params.get("highday_range", 25),
            "lowday_range": original_params.get("lowday_range", 17),
            "sortType": original_params.get("sortType", 1),
            "tradeType": original_params.get("tradeType", 1),
            "isReal": False
        }
        
        # 如果有日期范围，添加到参数中
        if "startDate" in backtest_params:
            params["startDate"] = backtest_params["startDate"]
        
        # 获取沪深300数据
        if params['ischeckhs300']:
            hs300_data = get_hs300_data(params['hs300_limit'], params['backCycle'], params.get('startDate'))
            params['hs300_data'] = hs300_data
        
        # 实例化策略并执行
        strategy = TradingStrategy(params)
        trade_log, cumulative_returns = strategy.execute()
        
        # 计算回撤和月度收益
        top_drawdowns = calculate_drawdowns(cumulative_returns)
        monthly_returns = calculate_monthly_returns(cumulative_returns)
        yearly_returns = calculate_yearly_returns(cumulative_returns)
        
        # 构建回测结果
        result = {
            'trade_log': trade_log,
            'cumulative_returns': cumulative_returns.to_dict('records'),
            'top_drawdowns': top_drawdowns,
            'monthly_returns': monthly_returns,
            'yearly_returns': yearly_returns
        }
        
        # 调试打印，查看原始结果结构
        print(f"DEBUG - 回测完成，trade_log长度: {len(trade_log)}, cumulative_returns长度: {len(cumulative_returns)}")
        
        # 计算评估指标
        metrics = calculate_metrics(result)
        
        # 构建结果，包含完整的回测数据
        original_params_with_cycle = {**original_params, "cycle": cycle}  # 添加周期到参数中
        return {
            "parameters": original_params_with_cycle,
            "metrics": metrics,
            "results": result
        }
    except Exception as e:
        print(f"回测执行错误: {str(e)}")
        import traceback
        traceback.print_exc()
        # 返回默认值（失败的回测）
        return {
            "parameters": {**original_params, "cycle": backtest_params.get("cycle", "day")},
            "metrics": {
                "finalReturn": -1.0,
                "annualizedReturn": -1.0,
                "maxDrawdown": 1.0,
                "sharpeRatio": -1.0,
                "volatility": 1.0,
                "winRate": 0.0
            },
            "results": {
                "trade_log": [],
                "cumulative_returns": [],
                "top_drawdowns": [],
                "monthly_returns": [],
                "yearly_returns": []
            }
        }

def calculate_metrics(backtest_result):
    """
    根据回测结果计算评估指标
    """
    metrics = {}
    
    # 如果有累计收益数据
    if "cumulative_returns" in backtest_result and backtest_result["cumulative_returns"]:
        cumulative_returns = backtest_result["cumulative_returns"]
        
        # 最终收益率
        if len(cumulative_returns) >= 2:
            first_value = cumulative_returns[0]["cumulative_return"]
            last_value = cumulative_returns[-1]["cumulative_return"]
            metrics["finalReturn"] = (last_value / first_value) - 1
            
            # 计算年化收益率
            first_date = pd.to_datetime(cumulative_returns[0]["date"])
            last_date = pd.to_datetime(cumulative_returns[-1]["date"])
            years = (last_date - first_date).days / 365
            metrics["annualizedReturn"] = (1 + metrics["finalReturn"]) ** (1 / max(years, 0.5)) - 1
            
            # 最大回撤
            max_drawdown = 0
            peak = cumulative_returns[0]["cumulative_return"]
            
            for point in cumulative_returns:
                current = point["cumulative_return"]
                peak = max(peak, current)
                drawdown = (peak - current) / peak
                max_drawdown = max(max_drawdown, drawdown)
            
            metrics["maxDrawdown"] = max_drawdown
            
            # 计算日收益率
            daily_returns = []
            for i in range(1, len(cumulative_returns)):
                prev_value = cumulative_returns[i-1]["cumulative_return"]
                current_value = cumulative_returns[i]["cumulative_return"]
                daily_returns.append((current_value / prev_value) - 1)
            
            # 波动率 (年化标准差)
            if daily_returns:
                daily_volatility = np.std(daily_returns)
                metrics["volatility"] = daily_volatility * np.sqrt(252)
                
                # 夏普比率 (假设无风险利率为3%)
                risk_free_rate = 0.03
                metrics["sharpeRatio"] = (metrics["annualizedReturn"] - risk_free_rate) / max(metrics["volatility"], 0.0001)
                
                # 胜率
                positive_returns = sum(1 for r in daily_returns if r > 0)
                metrics["winRate"] = positive_returns / len(daily_returns) if daily_returns else 0
            else:
                metrics["volatility"] = 0
                metrics["sharpeRatio"] = 0
                metrics["winRate"] = 0
        else:
            # 默认值
            metrics["finalReturn"] = 0
            metrics["annualizedReturn"] = 0
            metrics["maxDrawdown"] = 0
            metrics["volatility"] = 0
            metrics["sharpeRatio"] = 0
            metrics["winRate"] = 0
    else:
        # 默认值
        metrics["finalReturn"] = 0
        metrics["annualizedReturn"] = 0
        metrics["maxDrawdown"] = 0
        metrics["volatility"] = 0
        metrics["sharpeRatio"] = 0
        metrics["winRate"] = 0
    
    return metrics

def sort_results(results, optimization_target):
    """
    根据优化目标对结果进行排序
    """
    if optimization_target == "maxDrawdown":
        # 最大回撤是越小越好
        sorted_results = sorted(results, key=lambda x: x["metrics"]["maxDrawdown"])
    elif optimization_target == "volatility":
        # 波动率是越小越好
        sorted_results = sorted(results, key=lambda x: x["metrics"]["volatility"])
    else:
        # 其他指标是越大越好
        sorted_results = sorted(results, key=lambda x: x["metrics"][optimization_target], reverse=True)
    
    # 添加ID
    for i, result in enumerate(sorted_results):
        result["id"] = i + 1
        
        # 对于第一个结果（最优结果），确保包含完整的回测数据
        # 为了节省带宽，仅返回最优解的完整数据
        if i > 0 and "results" in result:
            # 对于非最优解，移除详细回测数据，仅保留摘要指标
            result.pop("results", None)
    
    return sorted_results 